use crate::dto;
use crate::service::Error;
use crate::service::candidate_path::CandidatePathService;
use crate::service::candidate_path_segment_list_manager::CandidatePathSegmentListManagerService;
use std::sync::Arc;

#[async_trait::async_trait]
pub trait CandidatePathManagerService: CandidatePathService {
    async fn list_info(
        &self,
        filter: &dto::core::candidate_path::Filter,
        offset: u64,
        limit: u64,
    ) -> Result<Vec<dto::candidate_path::CandidatePathInfo>, Error>;

    async fn get_info(
        &self,
        candidate_path_id: &str,
    ) -> Result<dto::candidate_path::CandidatePathInfo, Error>;

    async fn try_get_info(
        &self,
        candidate_path_id: &str,
    ) -> Result<Option<dto::candidate_path::CandidatePathInfo>, Error>;
}

pub struct DefaultCandidatePathManagerService {
    candidate_path_service: Arc<dyn CandidatePathService>,
    candidate_path_segment_list_manager_service: Arc<dyn CandidatePathSegmentListManagerService>,
}

impl DefaultCandidatePathManagerService {
    pub fn new(
        candidate_path_service: Arc<dyn CandidatePathService>,
        candidate_path_segment_list_manager_service: Arc<
            dyn CandidatePathSegmentListManagerService,
        >,
    ) -> Self {
        Self {
            candidate_path_service,
            candidate_path_segment_list_manager_service,
        }
    }
    async fn get_info_by_core(
        &self,
        candidate_path: dto::core::candidate_path::CandidatePathItem,
    ) -> Result<dto::candidate_path::CandidatePathInfo, Error> {
        let list = self
            .candidate_path_segment_list_manager_service
            .all_info(candidate_path.candidate_path_id.as_str())
            .await?;

        Ok(dto::candidate_path::CandidatePathInfo {
            inner: candidate_path,
            segment_lists: list,
        })
    }
}

#[async_trait::async_trait]
impl CandidatePathService for DefaultCandidatePathManagerService {
    async fn add(
        &self,
        params: &dto::core::candidate_path::AddParams,
    ) -> Result<dto::core::candidate_path::AddResponseData, Error> {
        self.candidate_path_service.add(params).await
    }

    async fn get(
        &self,
        candidate_path_id: &str,
    ) -> Result<dto::core::candidate_path::CandidatePathItem, Error> {
        self.candidate_path_service.get(candidate_path_id).await
    }

    async fn try_get(
        &self,
        candidate_path_id: &str,
    ) -> Result<Option<dto::core::candidate_path::CandidatePathItem>, Error> {
        self.candidate_path_service.try_get(candidate_path_id).await
    }

    async fn list(
        &self,
        filter: &dto::core::candidate_path::Filter,
        offset: u64,
        limit: u64,
    ) -> Result<Vec<dto::core::candidate_path::CandidatePathItem>, Error> {
        self.candidate_path_service
            .list(filter, offset, limit)
            .await
    }

    async fn update(
        &self,
        candidate_path_id: &str,
        params: &dto::core::candidate_path::UpdateParams,
    ) -> Result<(), Error> {
        self.candidate_path_service
            .update(candidate_path_id, params)
            .await
    }

    async fn delete(&self, candidate_path_id: &str) -> Result<(), Error> {
        self.candidate_path_segment_list_manager_service
            .delete_by_filter(&dto::core::candidate_path_segment_list::Filter {
                candidate_path_id: Some(candidate_path_id.to_string()),
                ..Default::default()
            })
            .await?;

        self.candidate_path_service.delete(candidate_path_id).await
    }

    async fn delete_by_filter(
        &self,
        filter: &dto::core::candidate_path::Filter,
    ) -> Result<(), Error> {
        let limit = wanmesh_common::dto::LIMIT_MAX;

        for offset in (0..).step_by(limit as usize) {
            let list = self.list(filter, offset, limit).await?;
            for item in &list {
                self.delete(item.candidate_path_id.as_str()).await?;
            }

            if (list.len() as u64) < limit {
                break;
            }
        }

        Ok(())
    }

    async fn count(&self, filter: &dto::core::candidate_path::Filter) -> Result<u64, Error> {
        self.candidate_path_service.count(filter).await
    }
}

#[async_trait::async_trait]
impl CandidatePathManagerService for DefaultCandidatePathManagerService {
    async fn list_info(
        &self,
        filter: &dto::core::candidate_path::Filter,
        offset: u64,
        limit: u64,
    ) -> Result<Vec<dto::candidate_path::CandidatePathInfo>, Error> {
        let core_list = self
            .candidate_path_service
            .list(filter, offset, limit)
            .await?;

        let mut list = Vec::new();
        for item in core_list {
            let info = self.get_info_by_core(item).await?;
            list.push(info);
        }

        Ok(list)
    }

    async fn get_info(
        &self,
        candidate_path_id: &str,
    ) -> Result<dto::candidate_path::CandidatePathInfo, Error> {
        let candidate_path = self.get(candidate_path_id).await?;
        self.get_info_by_core(candidate_path).await
    }

    async fn try_get_info(
        &self,
        candidate_path_id: &str,
    ) -> Result<Option<dto::candidate_path::CandidatePathInfo>, Error> {
        if let Some(candidate_path) = self.try_get(candidate_path_id).await? {
            let info = self.get_info_by_core(candidate_path).await?;
            return Ok(Some(info));
        }

        Ok(None)
    }
}
